
#include "NantSock.h"
#include "Debug.h"

using namespace std;
int64_t Nantx_Microtime()
{
	static int s_sec = -1, s_usec = -1;
	struct timeval tv;

	if ( s_sec == -1 )
	{
		gettimeofday ( &tv, NULL );
		s_sec = tv.tv_sec;
		s_usec = tv.tv_usec;
	}

	gettimeofday ( &tv, NULL );
	return int64_t(tv.tv_sec-s_sec)*int64_t(1000000) + int64_t(tv.tv_usec-s_usec);
}

int Nantx_Write_NOBlock(int sock, char *pBuffer, unsigned length )
{

	bool  m_bError = false; 
	if ( length == 0 )
		return 0;

	assert ( length > 0 );
	int m_iSent = 0;
/* 正常情况下，不会触发超时，所以可以设置稍大 */
	const int64_t tmMaxTimer = Nantx_Microtime() + 100*1000; // in microseconds
	while ( !m_bError )
	{
		int iRes = send(sock, pBuffer, length , 0);
		if ( iRes < 0 )
		{
			ERRO << "send() failed:"<< strerror(errno) << endl;;
			if ( errno != EAGAIN )
			{
				m_bError = true;
                                m_iSent = -100;
				break;
			}
		}
		else
		{
			m_iSent += iRes;
			pBuffer += iRes;
			length -= iRes;
			if ( length == 0 )
				break;
		}

		int64_t tmMicroLeft = tmMaxTimer - Nantx_Microtime();
		if ( tmMicroLeft > 0 )
	        {
                      DEBUG4("send res = ", iRes, ";  microLeft = ", tmMicroLeft);
                      continue;
                } 
		else
			break;
	}
	return (int)m_iSent;
}

int Nantx_Write(int sock, char *pBuffer, unsigned length )
{

	bool  m_bError = false; 
	if ( length == 0 )
		return 0;

	assert ( length > 0 );
	int m_iSent = 0;
	const int64_t tmMaxTimer = Nantx_Microtime() + 5*1000; // in microseconds
	while ( !m_bError )
	{
		int iRes = send(sock, pBuffer, length , 0);
		if ( iRes < 0 )
		{
			ERRO << "send() failed:"<< strerror(errno) << endl;;
			if ( errno != EAGAIN )
			{
				m_bError = true;
				break;
			}
		}
		else
		{
			m_iSent += iRes;
			pBuffer += iRes;
			length -= iRes;
			if ( length == 0 )
				break;
		}

		int64_t tmMicroLeft = tmMaxTimer - Nantx_Microtime();
		if ( tmMicroLeft > 0 )
		{
			fd_set fdWrite;
			FD_ZERO ( &fdWrite );
			 FD_SET (sock, &fdWrite );
			struct timeval tvTimeout;
			tvTimeout.tv_sec = (int)( tmMicroLeft / 1000000 );
			tvTimeout.tv_usec = (int)( tmMicroLeft % 1000000 );
			iRes = select ( sock+1, NULL, &fdWrite, NULL, &tvTimeout );
		}
		else
			iRes = 0;
		switch ( iRes )
		{
			case 1: // ready for writing
				break;

			case 0: // timed out
				{
					ERRO <<  "timed out while trying to flush network buffers" << endl ;
					m_bError = true;
					break;
				}

			case -1: // error
				{
					if ( errno == EINTR )
						break;
					ERRO <<  "select() failed: " << strerror(errno) << endl ;
					m_bError = true;
					break;
				}
		}
	}
	return (int)m_iSent;
}

int Nantx_Read(int sock, char **wbuf, unsigned &wsize, unsigned &wbytes, int timeout)
{

	int64_t tmMaxTimer = Nantx_Microtime() + 1000*timeout ; // in microseconds
	if(*wbuf == NULL)
	{
		wsize = 2048;
		*wbuf = (char *)malloc(wsize);
		if(*wbuf == NULL)
			return -1;
	}          
        wbytes = 0;
	int iRes = -1;
	while ( true) // iLeftBytes>0 )
	{
		const int64_t tmMicroLeft = tmMaxTimer - Nantx_Microtime();
		if ( tmMicroLeft<=0 )
		{
                        DEBUG3("read length = ", wbytes, "; timedout"); 
			break; // timed out
                }

		// try to receive next chunk
                int avail = wsize - wbytes;
		if (avail < 128 ) {
			char *new_rbuf = (char *)realloc(*wbuf, wsize * 2);
			if (!new_rbuf) {
				wbytes = 0; /* ignore what we read */
				ERRO<< "SERVER_ERROR out of memory reading request" << endl;
				return 0;
			}
			*wbuf = new_rbuf;
			wsize *= 2;
                        avail = wsize - wbytes;
		}

		iRes = recv(sock, *wbuf+wbytes, avail , 0);
                DEBUG2("recv return = ", iRes);
		if(iRes > 0)
		{
			wbytes += iRes;
			continue;
		}	   
		// if there was eof, we're done
		if ( iRes==0 )
		{
                        break;
                        //return (int)wbytes;
		//	ERRO << strerror( ECONNRESET ) <<": or socket lost!" << endl;;
		}

		// if there was EINTR, retry
		if ( iRes==-1 )
		{
			if ( errno == EINTR || errno == EAGAIN )
				continue;

			ERRO << strerror( errno ) << endl;
			return -1;
		}

	}
	return  (int)wbytes;
}
